#!/bin/bash

# AIQuant 系统备份脚本
# 用途: 自动化系统备份，支持增量备份和云存储
# 作者: AIQuant Team
# 版本: 1.0.0

set -e

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 配置变量
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
BACKUP_BASE_DIR="/opt/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="$BACKUP_BASE_DIR/$TIMESTAMP"
RETENTION_DAYS=30
MAX_BACKUP_SIZE="50G"

# 备份类型
BACKUP_TYPE="full"  # full, incremental, differential

# 压缩设置
COMPRESSION_LEVEL=6
USE_ENCRYPTION=false
ENCRYPTION_KEY=""

# 云存储设置
ENABLE_CLOUD_BACKUP=false
AWS_S3_BUCKET=""
AWS_S3_PREFIX="aiquant-backups"
AZURE_CONTAINER=""
GCS_BUCKET=""

# 通知设置
ENABLE_NOTIFICATIONS=true
SLACK_WEBHOOK_URL=""
EMAIL_RECIPIENTS=""

# 日志函数
log_info() {
    echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}

# 错误处理
error_exit() {
    log_error "$1"
    send_notification "error" "备份失败: $1"
    exit 1
}

# 显示帮助信息
show_help() {
    cat << EOF
AIQuant 系统备份脚本

用法: $0 [OPTIONS]

选项:
    -h, --help              显示帮助信息
    -t, --type TYPE         备份类型 (full|incremental|differential)
    -d, --dir DIR           备份目录 (默认: $BACKUP_BASE_DIR)
    -r, --retention DAYS    保留天数 (默认: $RETENTION_DAYS)
    -c, --compress LEVEL    压缩级别 1-9 (默认: $COMPRESSION_LEVEL)
    -e, --encrypt           启用加密
    -k, --key KEY           加密密钥
    --cloud                 启用云备份
    --s3-bucket BUCKET      AWS S3存储桶
    --azure-container NAME  Azure容器名称
    --gcs-bucket BUCKET     Google Cloud Storage存储桶
    --no-db                 跳过数据库备份
    --no-files              跳过文件备份
    --no-config             跳过配置备份
    --verify                验证备份完整性
    --dry-run               模拟运行，不实际备份

备份类型说明:
    full         完整备份 (默认)
    incremental  增量备份 (基于上次备份)
    differential 差异备份 (基于上次完整备份)

示例:
    $0                                    # 完整备份
    $0 -t incremental                     # 增量备份
    $0 --encrypt -k mykey123              # 加密备份
    $0 --cloud --s3-bucket my-bucket      # 云备份
    $0 --verify                           # 备份并验证

EOF
}

# 解析命令行参数
parse_args() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help)
                show_help
                exit 0
                ;;
            -t|--type)
                BACKUP_TYPE="$2"
                shift 2
                ;;
            -d|--dir)
                BACKUP_BASE_DIR="$2"
                BACKUP_DIR="$BACKUP_BASE_DIR/$TIMESTAMP"
                shift 2
                ;;
            -r|--retention)
                RETENTION_DAYS="$2"
                shift 2
                ;;
            -c|--compress)
                COMPRESSION_LEVEL="$2"
                shift 2
                ;;
            -e|--encrypt)
                USE_ENCRYPTION=true
                shift
                ;;
            -k|--key)
                ENCRYPTION_KEY="$2"
                USE_ENCRYPTION=true
                shift 2
                ;;
            --cloud)
                ENABLE_CLOUD_BACKUP=true
                shift
                ;;
            --s3-bucket)
                AWS_S3_BUCKET="$2"
                ENABLE_CLOUD_BACKUP=true
                shift 2
                ;;
            --azure-container)
                AZURE_CONTAINER="$2"
                ENABLE_CLOUD_BACKUP=true
                shift 2
                ;;
            --gcs-bucket)
                GCS_BUCKET="$2"
                ENABLE_CLOUD_BACKUP=true
                shift 2
                ;;
            --no-db)
                SKIP_DATABASE=true
                shift
                ;;
            --no-files)
                SKIP_FILES=true
                shift
                ;;
            --no-config)
                SKIP_CONFIG=true
                shift
                ;;
            --verify)
                VERIFY_BACKUP=true
                shift
                ;;
            --dry-run)
                DRY_RUN=true
                shift
                ;;
            -*)
                error_exit "未知选项: $1"
                ;;
            *)
                shift
                ;;
        esac
    done
}

# 加载配置
load_config() {
    local config_file="$PROJECT_ROOT/.backup_config"
    if [[ -f "$config_file" ]]; then
        source "$config_file"
        log_info "已加载备份配置文件"
    fi
    
    # 从环境变量加载配置
    BACKUP_BASE_DIR=${BACKUP_BASE_DIR:-$BACKUP_BASE_DIR}
    RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-$RETENTION_DAYS}
    AWS_S3_BUCKET=${BACKUP_S3_BUCKET:-$AWS_S3_BUCKET}
    SLACK_WEBHOOK_URL=${BACKUP_SLACK_WEBHOOK:-$SLACK_WEBHOOK_URL}
    EMAIL_RECIPIENTS=${BACKUP_EMAIL_RECIPIENTS:-$EMAIL_RECIPIENTS}
}

# 预检查
pre_backup_check() {
    log_info "执行备份前检查..."
    
    # 检查必需的命令
    local required_commands=("docker" "docker-compose" "tar" "gzip")
    for cmd in "${required_commands[@]}"; do
        if ! command -v "$cmd" &> /dev/null; then
            error_exit "缺少必需命令: $cmd"
        fi
    done
    
    # 检查Docker服务
    if ! docker info &> /dev/null; then
        error_exit "Docker服务未运行"
    fi
    
    # 检查磁盘空间
    local available_space=$(df "$BACKUP_BASE_DIR" 2>/dev/null | awk 'NR==2{print $4}' || echo "0")
    local required_space=10485760  # 10GB in KB
    if [[ $available_space -lt $required_space ]]; then
        error_exit "备份目录磁盘空间不足，需要至少10GB可用空间"
    fi
    
    # 检查备份类型
    if [[ ! "$BACKUP_TYPE" =~ ^(full|incremental|differential)$ ]]; then
        error_exit "无效的备份类型: $BACKUP_TYPE"
    fi
    
    # 检查压缩级别
    if [[ ! "$COMPRESSION_LEVEL" =~ ^[1-9]$ ]]; then
        error_exit "无效的压缩级别: $COMPRESSION_LEVEL"
    fi
    
    # 检查加密设置
    if [[ "$USE_ENCRYPTION" == "true" && -z "$ENCRYPTION_KEY" ]]; then
        error_exit "启用加密但未提供密钥"
    fi
    
    # 检查云存储工具
    if [[ "$ENABLE_CLOUD_BACKUP" == "true" ]]; then
        if [[ -n "$AWS_S3_BUCKET" && ! command -v aws &> /dev/null ]]; then
            error_exit "AWS CLI未安装"
        fi
        if [[ -n "$AZURE_CONTAINER" && ! command -v az &> /dev/null ]]; then
            error_exit "Azure CLI未安装"
        fi
        if [[ -n "$GCS_BUCKET" && ! command -v gsutil &> /dev/null ]]; then
            error_exit "Google Cloud SDK未安装"
        fi
    fi
    
    log_success "预检查完成"
}

# 创建备份目录
create_backup_dir() {
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将创建备份目录: $BACKUP_DIR"
        return 0
    fi
    
    log_info "创建备份目录: $BACKUP_DIR"
    mkdir -p "$BACKUP_DIR"/{database,files,config,logs}
    
    # 设置权限
    chmod 750 "$BACKUP_DIR"
    
    log_success "备份目录创建完成"
}

# 备份数据库
backup_database() {
    if [[ "$SKIP_DATABASE" == "true" ]]; then
        log_info "跳过数据库备份"
        return 0
    fi
    
    log_info "开始备份数据库..."
    
    cd "$PROJECT_ROOT"
    
    # 检查数据库连接
    if ! docker-compose exec -T postgres pg_isready -U aiquant; then
        log_warning "PostgreSQL数据库不可用，跳过备份"
        return 0
    fi
    
    local db_backup_file="$BACKUP_DIR/database/postgres_$(date +%Y%m%d_%H%M%S).sql"
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将备份PostgreSQL到: $db_backup_file"
    else
        # 备份PostgreSQL
        log_info "备份PostgreSQL数据库..."
        docker-compose exec -T postgres pg_dump \
            -U aiquant \
            -d aiquant \
            --verbose \
            --format=custom \
            --compress=$COMPRESSION_LEVEL \
            > "$db_backup_file.dump" || error_exit "PostgreSQL备份失败"
        
        # 同时创建SQL格式备份
        docker-compose exec -T postgres pg_dump \
            -U aiquant \
            -d aiquant \
            --verbose \
            --format=plain \
            > "$db_backup_file" || log_warning "PostgreSQL SQL备份失败"
        
        # 压缩SQL备份
        gzip "$db_backup_file"
        
        log_success "PostgreSQL备份完成"
    fi
    
    # 备份TimescaleDB（如果存在）
    if docker-compose ps timescaledb | grep -q "Up"; then
        local tsdb_backup_file="$BACKUP_DIR/database/timescaledb_$(date +%Y%m%d_%H%M%S).sql"
        
        if [[ "$DRY_RUN" == "true" ]]; then
            log_info "[DRY RUN] 将备份TimescaleDB到: $tsdb_backup_file"
        else
            log_info "备份TimescaleDB..."
            docker-compose exec -T timescaledb pg_dump \
                -U aiquant \
                -d aiquant_ts \
                --verbose \
                --format=plain \
                > "$tsdb_backup_file" || log_warning "TimescaleDB备份失败"
            
            gzip "$tsdb_backup_file"
            log_success "TimescaleDB备份完成"
        fi
    fi
    
    # 备份Redis
    if docker-compose ps redis | grep -q "Up"; then
        local redis_backup_file="$BACKUP_DIR/database/redis_$(date +%Y%m%d_%H%M%S).rdb"
        
        if [[ "$DRY_RUN" == "true" ]]; then
            log_info "[DRY RUN] 将备份Redis到: $redis_backup_file"
        else
            log_info "备份Redis数据..."
            docker-compose exec -T redis redis-cli --rdb /tmp/redis_backup.rdb
            docker-compose exec -T redis cat /tmp/redis_backup.rdb > "$redis_backup_file" || log_warning "Redis备份失败"
            log_success "Redis备份完成"
        fi
    fi
}

# 备份应用文件
backup_files() {
    if [[ "$SKIP_FILES" == "true" ]]; then
        log_info "跳过文件备份"
        return 0
    fi
    
    log_info "开始备份应用文件..."
    
    local files_backup_file="$BACKUP_DIR/files/application_files.tar.gz"
    
    # 定义要备份的目录和文件
    local backup_paths=(
        "data"
        "logs"
        "uploads"
        "models"
        "strategies"
        "backtest_results"
    )
    
    # 创建排除文件列表
    local exclude_file="/tmp/backup_exclude_$$"
    cat > "$exclude_file" << 'EOF'
*.pyc
__pycache__
.git
.gitignore
node_modules
.env
*.log
*.tmp
.DS_Store
Thumbs.db
EOF
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将备份应用文件到: $files_backup_file"
        log_info "[DRY RUN] 备份路径: ${backup_paths[*]}"
    else
        cd "$PROJECT_ROOT"
        
        # 根据备份类型选择备份方式
        local tar_options=""
        case "$BACKUP_TYPE" in
            "incremental")
                local last_backup=$(find "$BACKUP_BASE_DIR" -name "*.snar" -type f | sort | tail -1)
                if [[ -n "$last_backup" ]]; then
                    tar_options="--listed-incremental=$BACKUP_DIR/files/incremental.snar"
                    cp "$last_backup" "$BACKUP_DIR/files/incremental.snar"
                else
                    log_warning "未找到增量备份基准，执行完整备份"
                    tar_options="--listed-incremental=$BACKUP_DIR/files/incremental.snar"
                fi
                ;;
            "differential")
                local last_full_backup=$(find "$BACKUP_BASE_DIR" -name "*full*.snar" -type f | sort | tail -1)
                if [[ -n "$last_full_backup" ]]; then
                    tar_options="--listed-incremental=$last_full_backup"
                else
                    log_warning "未找到完整备份基准，执行完整备份"
                    tar_options="--listed-incremental=$BACKUP_DIR/files/full.snar"
                fi
                ;;
            "full")
                tar_options="--listed-incremental=$BACKUP_DIR/files/full.snar"
                ;;
        esac
        
        # 执行备份
        tar $tar_options \
            --exclude-from="$exclude_file" \
            -czf "$files_backup_file" \
            "${backup_paths[@]}" 2>/dev/null || log_warning "部分文件备份失败"
        
        # 清理临时文件
        rm -f "$exclude_file"
        
        log_success "应用文件备份完成"
    fi
}

# 备份配置文件
backup_config() {
    if [[ "$SKIP_CONFIG" == "true" ]]; then
        log_info "跳过配置备份"
        return 0
    fi
    
    log_info "开始备份配置文件..."
    
    local config_backup_file="$BACKUP_DIR/config/configuration.tar.gz"
    
    # 定义要备份的配置文件
    local config_files=(
        ".env.production"
        ".env.example"
        "docker-compose.yml"
        "docker-compose.prod.yml"
        "Dockerfile"
        "requirements.txt"
        "package.json"
        "nginx"
        "ssl"
        "monitoring"
        "scripts"
        "deployment"
    )
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将备份配置文件到: $config_backup_file"
    else
        cd "$PROJECT_ROOT"
        
        # 创建配置文件备份
        tar -czf "$config_backup_file" \
            "${config_files[@]}" 2>/dev/null || log_warning "部分配置文件备份失败"
        
        # 备份系统配置
        local system_config_file="$BACKUP_DIR/config/system_config.tar.gz"
        tar -czf "$system_config_file" \
            -C / \
            etc/nginx \
            etc/ssl \
            etc/systemd/system/aiquant* \
            2>/dev/null || log_warning "系统配置备份失败"
        
        log_success "配置文件备份完成"
    fi
}

# 备份日志
backup_logs() {
    log_info "开始备份日志文件..."
    
    local logs_backup_file="$BACKUP_DIR/logs/application_logs.tar.gz"
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将备份日志到: $logs_backup_file"
    else
        cd "$PROJECT_ROOT"
        
        # 备份应用日志
        if [[ -d "logs" ]]; then
            tar -czf "$logs_backup_file" logs/ || log_warning "应用日志备份失败"
        fi
        
        # 备份Docker日志
        local docker_logs_file="$BACKUP_DIR/logs/docker_logs.tar.gz"
        docker-compose logs --no-color > "$BACKUP_DIR/logs/docker_compose.log" 2>/dev/null || log_warning "Docker日志备份失败"
        
        # 备份系统日志
        local system_logs_file="$BACKUP_DIR/logs/system_logs.tar.gz"
        tar -czf "$system_logs_file" \
            -C / \
            var/log/nginx \
            var/log/syslog \
            var/log/auth.log \
            2>/dev/null || log_warning "系统日志备份失败"
        
        log_success "日志备份完成"
    fi
}

# 加密备份
encrypt_backup() {
    if [[ "$USE_ENCRYPTION" != "true" ]]; then
        return 0
    fi
    
    log_info "加密备份文件..."
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将加密备份目录: $BACKUP_DIR"
        return 0
    fi
    
    # 使用GPG加密
    if command -v gpg &> /dev/null; then
        find "$BACKUP_DIR" -type f -name "*.tar.gz" -o -name "*.sql" -o -name "*.dump" | while read file; do
            log_info "加密文件: $(basename "$file")"
            gpg --symmetric --cipher-algo AES256 --compress-algo 1 --s2k-mode 3 \
                --s2k-digest-algo SHA512 --s2k-count 65536 \
                --passphrase "$ENCRYPTION_KEY" \
                --batch --yes \
                "$file" || log_warning "文件加密失败: $file"
            
            # 删除原文件
            rm -f "$file"
        done
        
        log_success "备份加密完成"
    else
        log_warning "GPG未安装，跳过加密"
    fi
}

# 创建备份清单
create_backup_manifest() {
    log_info "创建备份清单..."
    
    local manifest_file="$BACKUP_DIR/backup_manifest.txt"
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将创建备份清单: $manifest_file"
        return 0
    fi
    
    cat > "$manifest_file" << EOF
# AIQuant 系统备份清单
# 备份时间: $(date)
# 备份类型: $BACKUP_TYPE
# 备份目录: $BACKUP_DIR
# 压缩级别: $COMPRESSION_LEVEL
# 加密状态: $USE_ENCRYPTION

## 系统信息
操作系统: $(uname -a)
Docker版本: $(docker --version)
Docker Compose版本: $(docker-compose --version)

## 服务状态
EOF
    
    # 添加服务状态
    cd "$PROJECT_ROOT"
    echo "### Docker容器状态" >> "$manifest_file"
    docker-compose ps >> "$manifest_file" 2>/dev/null || echo "无法获取容器状态" >> "$manifest_file"
    
    # 添加文件清单
    echo -e "\n### 备份文件清单" >> "$manifest_file"
    find "$BACKUP_DIR" -type f -exec ls -lh {} \; >> "$manifest_file"
    
    # 计算校验和
    echo -e "\n### 文件校验和" >> "$manifest_file"
    find "$BACKUP_DIR" -type f -name "*.tar.gz" -o -name "*.sql*" -o -name "*.dump" -o -name "*.rdb" | while read file; do
        if [[ -f "$file" ]]; then
            echo "$(sha256sum "$file")" >> "$manifest_file"
        fi
    done
    
    log_success "备份清单创建完成"
}

# 验证备份
verify_backup() {
    if [[ "$VERIFY_BACKUP" != "true" ]]; then
        return 0
    fi
    
    log_info "验证备份完整性..."
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将验证备份完整性"
        return 0
    fi
    
    local verification_failed=false
    
    # 验证压缩文件
    find "$BACKUP_DIR" -name "*.tar.gz" | while read file; do
        if ! tar -tzf "$file" >/dev/null 2>&1; then
            log_error "压缩文件损坏: $file"
            verification_failed=true
        fi
    done
    
    # 验证数据库备份
    find "$BACKUP_DIR" -name "*.dump" | while read file; do
        if ! file "$file" | grep -q "PostgreSQL custom database dump"; then
            log_error "数据库备份文件损坏: $file"
            verification_failed=true
        fi
    done
    
    # 验证校验和
    local manifest_file="$BACKUP_DIR/backup_manifest.txt"
    if [[ -f "$manifest_file" ]]; then
        grep "^[a-f0-9]\{64\}" "$manifest_file" | while read checksum file; do
            if [[ -f "$file" ]]; then
                local current_checksum=$(sha256sum "$file" | cut -d' ' -f1)
                if [[ "$checksum" != "$current_checksum" ]]; then
                    log_error "文件校验和不匹配: $file"
                    verification_failed=true
                fi
            fi
        done
    fi
    
    if [[ "$verification_failed" == "true" ]]; then
        error_exit "备份验证失败"
    else
        log_success "备份验证通过"
    fi
}

# 上传到云存储
upload_to_cloud() {
    if [[ "$ENABLE_CLOUD_BACKUP" != "true" ]]; then
        return 0
    fi
    
    log_info "上传备份到云存储..."
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将上传到云存储"
        return 0
    fi
    
    # AWS S3
    if [[ -n "$AWS_S3_BUCKET" ]]; then
        log_info "上传到AWS S3: $AWS_S3_BUCKET"
        aws s3 sync "$BACKUP_DIR/" "s3://$AWS_S3_BUCKET/$AWS_S3_PREFIX/$(basename $BACKUP_DIR)/" \
            --storage-class STANDARD_IA || log_warning "S3上传失败"
    fi
    
    # Azure Blob Storage
    if [[ -n "$AZURE_CONTAINER" ]]; then
        log_info "上传到Azure Blob Storage: $AZURE_CONTAINER"
        az storage blob upload-batch \
            --destination "$AZURE_CONTAINER" \
            --source "$BACKUP_DIR" \
            --destination-path "$(basename $BACKUP_DIR)" || log_warning "Azure上传失败"
    fi
    
    # Google Cloud Storage
    if [[ -n "$GCS_BUCKET" ]]; then
        log_info "上传到Google Cloud Storage: $GCS_BUCKET"
        gsutil -m cp -r "$BACKUP_DIR" "gs://$GCS_BUCKET/$(basename $BACKUP_DIR)/" || log_warning "GCS上传失败"
    fi
    
    log_success "云存储上传完成"
}

# 清理旧备份
cleanup_old_backups() {
    log_info "清理旧备份文件..."
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] 将清理超过 $RETENTION_DAYS 天的备份"
        find "$BACKUP_BASE_DIR" -type d -name "20*" -mtime +$RETENTION_DAYS | head -5
        return 0
    fi
    
    # 清理本地旧备份
    local deleted_count=0
    find "$BACKUP_BASE_DIR" -type d -name "20*" -mtime +$RETENTION_DAYS | while read old_backup; do
        log_info "删除旧备份: $(basename "$old_backup")"
        rm -rf "$old_backup"
        ((deleted_count++))
    done
    
    if [[ $deleted_count -gt 0 ]]; then
        log_success "已清理 $deleted_count 个旧备份"
    else
        log_info "没有需要清理的旧备份"
    fi
    
    # 清理云存储旧备份
    if [[ "$ENABLE_CLOUD_BACKUP" == "true" && -n "$AWS_S3_BUCKET" ]]; then
        log_info "清理S3旧备份..."
        aws s3 ls "s3://$AWS_S3_BUCKET/$AWS_S3_PREFIX/" | \
        awk '{print $2}' | \
        while read backup_dir; do
            local backup_date=$(echo "$backup_dir" | grep -oE '[0-9]{8}')
            if [[ -n "$backup_date" ]]; then
                local days_old=$(( ($(date +%s) - $(date -d "$backup_date" +%s)) / 86400 ))
                if [[ $days_old -gt $RETENTION_DAYS ]]; then
                    log_info "删除S3旧备份: $backup_dir"
                    aws s3 rm "s3://$AWS_S3_BUCKET/$AWS_S3_PREFIX/$backup_dir" --recursive
                fi
            fi
        done
    fi
}

# 发送通知
send_notification() {
    if [[ "$ENABLE_NOTIFICATIONS" != "true" ]]; then
        return 0
    fi
    
    local status="$1"
    local message="$2"
    
    # Slack通知
    if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
        local color="good"
        if [[ "$status" == "error" ]]; then
            color="danger"
        elif [[ "$status" == "warning" ]]; then
            color="warning"
        fi
        
        curl -X POST "$SLACK_WEBHOOK_URL" \
            -H 'Content-type: application/json' \
            --data "{
                \"attachments\": [{
                    \"color\": \"$color\",
                    \"title\": \"AIQuant系统备份\",
                    \"text\": \"$message\",
                    \"fields\": [
                        {\"title\": \"备份类型\", \"value\": \"$BACKUP_TYPE\", \"short\": true},
                        {\"title\": \"备份目录\", \"value\": \"$BACKUP_DIR\", \"short\": true},
                        {\"title\": \"时间\", \"value\": \"$(date)\", \"short\": false}
                    ]
                }]
            }" || log_warning "Slack通知发送失败"
    fi
    
    # 邮件通知
    if [[ -n "$EMAIL_RECIPIENTS" ]]; then
        echo "$message" | mail -s "AIQuant备份通知 - $status" "$EMAIL_RECIPIENTS" || log_warning "邮件通知发送失败"
    fi
}

# 主函数
main() {
    log_info "开始AIQuant系统备份"
    log_info "备份类型: $BACKUP_TYPE"
    log_info "备份目录: $BACKUP_DIR"
    log_info "时间: $(date)"
    
    # 解析命令行参数
    parse_args "$@"
    
    # 加载配置
    load_config
    
    # 执行预检查
    pre_backup_check
    
    # 创建备份目录
    create_backup_dir
    
    # 记录开始时间
    local start_time=$(date +%s)
    
    # 执行备份
    backup_database
    backup_files
    backup_config
    backup_logs
    
    # 加密备份
    encrypt_backup
    
    # 创建备份清单
    create_backup_manifest
    
    # 验证备份
    verify_backup
    
    # 上传到云存储
    upload_to_cloud
    
    # 清理旧备份
    cleanup_old_backups
    
    # 计算备份时间
    local end_time=$(date +%s)
    local duration=$((end_time - start_time))
    local backup_size=$(du -sh "$BACKUP_DIR" 2>/dev/null | cut -f1 || echo "未知")
    
    # 发送成功通知
    local success_message="AIQuant系统备份成功完成
备份类型: $BACKUP_TYPE
备份大小: $backup_size
耗时: ${duration}秒
备份目录: $BACKUP_DIR"
    
    send_notification "success" "$success_message"
    
    log_success "备份完成！"
    log_info "备份大小: $backup_size"
    log_info "耗时: ${duration}秒"
    log_info "备份位置: $BACKUP_DIR"
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "这是模拟运行，没有实际执行备份操作"
    fi
}

# 信号处理
cleanup_on_exit() {
    local exit_code=$?
    if [[ $exit_code -ne 0 ]]; then
        log_error "备份过程中断，退出码: $exit_code"
        send_notification "error" "备份过程异常中断，退出码: $exit_code"
    fi
    
    # 清理临时文件
    rm -f /tmp/backup_exclude_$$
    
    exit $exit_code
}

# 设置信号处理
trap cleanup_on_exit EXIT
trap 'error_exit "备份被用户中断"' INT TERM

# 检查是否直接运行脚本
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi